package org.zebra.redisson;

import cn.hutool.extra.spring.SpringUtil;
import org.redisson.api.RedissonClient;
import org.redisson.client.codec.Codec;
import org.redisson.client.codec.StringCodec;
import org.redisson.codec.CompositeCodec;
import org.redisson.config.ReadMode;
import org.redisson.config.SubscriptionMode;
import org.redisson.spring.data.connection.RedissonConnectionFactory;
import org.redisson.spring.starter.RedissonAutoConfigurationCustomizer;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.core.env.Environment;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.zebra.cache.config.CacheConfig;
import org.zebra.redisson.codec.MsgPackPlusJacksonCodec;
import org.zebra.redisson.codec.TypedJsonPlusJacksonCodec;

/**
 * @author zhanghongbin
 */
@AutoConfiguration(before = {CacheConfig.class})
public class RedissonConfig {
    // 连接空闲超时，单位：毫秒
    private static final int IDLE_CONNECTION_TIMEOUT = 10000;
    // 命令等待超时，单位：毫秒
    private static final int TIMEOUT = 5000;
    // 发布和订阅连接池大小
    private static final int SUBSCRIPTION_CONNECTION_POOL_SIZE = 50;
    private static final int CONNECTION_MINIMUM_IDEL_SIZE = 8;
    private static final int CONNECTION_POOL_SIZE = 32;

    @Bean
    public RedissonAutoConfigurationCustomizer redissonDefaultCustomizer() {
        return config -> {
            String serializer = SpringUtil.getBean(Environment.class)
                    .getProperty("zebra.cache.serializer.msgpack.enabled", "false");
            Codec jsonCodec;
            if (serializer.equals("true")) {
                jsonCodec = new MsgPackPlusJacksonCodec();
            } else {
                jsonCodec = new TypedJsonPlusJacksonCodec();
            }
            // 组合序列化 key 使用 String 内容使用通用 json 格式
            CompositeCodec codec = new CompositeCodec(StringCodec.INSTANCE, jsonCodec, jsonCodec);
            int threadNum = (Runtime.getRuntime().availableProcessors() + 1) * 2;
            config.setThreads(threadNum)
                    .setNettyThreads(threadNum)
                    // 缓存 Lua 脚本 减少网络传输(redisson 大部分的功能都是基于 Lua 脚本实现)
                    .setUseScriptCache(true)
                    .setCodec(codec);
            if (config.isClusterConfig()) {
                config.useClusterServers()
                        .setTimeout(TIMEOUT)
                        .setIdleConnectionTimeout(IDLE_CONNECTION_TIMEOUT)
                        .setSubscriptionConnectionPoolSize(SUBSCRIPTION_CONNECTION_POOL_SIZE)
                        .setMasterConnectionMinimumIdleSize(CONNECTION_POOL_SIZE)
                        .setMasterConnectionPoolSize(CONNECTION_POOL_SIZE * 2)
                        .setSlaveConnectionMinimumIdleSize(CONNECTION_POOL_SIZE)
                        .setSlaveConnectionPoolSize(CONNECTION_POOL_SIZE * 2)
                        .setReadMode(ReadMode.SLAVE)
                        .setSubscriptionMode(SubscriptionMode.MASTER);
            } else {
                // 使用单机模式
                config.useSingleServer()
                        .setTimeout(TIMEOUT)
                        .setIdleConnectionTimeout(IDLE_CONNECTION_TIMEOUT)
                        .setSubscriptionConnectionPoolSize(SUBSCRIPTION_CONNECTION_POOL_SIZE)
                        .setConnectionMinimumIdleSize(CONNECTION_MINIMUM_IDEL_SIZE)
                        .setConnectionPoolSize(CONNECTION_POOL_SIZE);
            }
        };
    }

    @Bean
    public RedisConnectionFactory redisConnectionFactory(@Autowired RedissonClient redissonClient) {
        return new RedissonConnectionFactory(redissonClient);
    }
}
